"""Auth serve models"""
import json
from datetime import datetime
from typing import Any, Dict, List, Optional, Union

from sqlalchemy import Column, DateTime, Integer, String, Text, UniqueConstraint, text
from sqlalchemy.orm import Session

from dbgpt.storage.metadata import BaseDao, Model

from ..api.schemas import (
    UserRequest,
    UserResponse,
    RoleRequest,
    RoleResponse,
    MenuRequest,
    MenuResponse,
)
from ..config import (
    SERVER_USER_TABLE_NAME,
    SERVER_ROLE_TABLE_NAME,
    SERVER_USER_ROLE_TABLE_NAME,
    SERVER_MENU_TABLE_NAME,
    SERVER_ROLE_MENU_TABLE_NAME,
    SERVER_DATA_PERMISSION_TABLE_NAME,
    SERVER_LOGIN_LOG_TABLE_NAME,
    SERVER_OPERATION_LOG_TABLE_NAME,
    ServeConfig,
)


# ========================================
# User Entity
# ========================================
class UserEntity(Model):
    """User entity"""

    __tablename__ = SERVER_USER_TABLE_NAME

    id = Column(Integer, primary_key=True, autoincrement=True, comment="User ID")
    username = Column(String(50), nullable=False, unique=True, comment="Username")
    password = Column(String(255), nullable=False, comment="Password (encrypted)")
    nick_name = Column(String(100), comment="Nickname")
    email = Column(String(100), comment="Email")
    phone = Column(String(20), comment="Phone number")
    avatar_url = Column(String(255), comment="Avatar URL")
    status = Column(Integer, default=1, comment="Status: 1=Active, 0=Disabled")
    is_deleted = Column(Integer, default=0, comment="Delete flag: 0=Normal, 1=Deleted")
    remark = Column(String(500), comment="Remark")
    created_by = Column(Integer, comment="Creator ID")
    updated_by = Column(Integer, comment="Updater ID")
    gmt_created = Column(DateTime, default=datetime.now, comment="Create time")
    gmt_modified = Column(
        DateTime, default=datetime.now, onupdate=datetime.now, comment="Update time"
    )

    def __repr__(self):
        return f"UserEntity(id={self.id}, username='{self.username}', nick_name='{self.nick_name}')"


class UserDao(BaseDao[UserEntity, UserRequest, UserResponse]):
    """User DAO"""

    def __init__(self, serve_config: ServeConfig):
        super().__init__()
        self._serve_config = serve_config

    def from_request(
        self, request: Union[UserRequest, Dict[str, Any]]
    ) -> UserEntity:
        """Convert request to entity"""
        request_dict = (
            request.to_dict() if isinstance(request, UserRequest) else request
        )
        # Filter out fields that don't belong to UserEntity
        entity_fields = {
            'id', 'username', 'password', 'nick_name', 'email', 'phone',
            'avatar_url', 'status', 'is_deleted', 'remark', 'created_by', 'updated_by'
        }
        filtered_dict = {k: v for k, v in request_dict.items() if k in entity_fields}
        entity = UserEntity(**filtered_dict)
        return entity

    def to_request(self, entity: UserEntity) -> UserRequest:
        """Convert entity to request"""
        return UserRequest(
            id=entity.id,
            username=entity.username,
            password=entity.password,
            nick_name=entity.nick_name,
            email=entity.email,
            phone=entity.phone,
            avatar_url=entity.avatar_url,
            status=entity.status,
            remark=entity.remark,
        )

    def to_response(self, entity: UserEntity) -> UserResponse:
        """Convert entity to response"""
        return UserResponse(
            id=entity.id,
            username=entity.username,
            nick_name=entity.nick_name,
            email=entity.email,
            phone=entity.phone,
            avatar_url=entity.avatar_url,
            status=entity.status,
            remark=entity.remark,
            gmt_created=entity.gmt_created.isoformat() if entity.gmt_created else None,
            gmt_modified=entity.gmt_modified.isoformat()
            if entity.gmt_modified
            else None,
        )

    def get_by_username(
        self, session: Session, username: str
    ) -> Optional[UserEntity]:
        """Get user by username"""
        return (
            session.query(UserEntity)
            .filter(UserEntity.username == username, UserEntity.is_deleted == 0)
            .first()
        )

    def get_user_roles(self, session: Session, user_id: int) -> List[int]:
        """Get user role IDs"""
        results = (
            session.query(UserRoleEntity.role_id)
            .filter(UserRoleEntity.user_id == user_id)
            .all()
        )
        return [r[0] for r in results]


# ========================================
# Role Entity
# ========================================
class RoleEntity(Model):
    """Role entity"""

    __tablename__ = SERVER_ROLE_TABLE_NAME

    id = Column(Integer, primary_key=True, autoincrement=True, comment="Role ID")
    role_code = Column(String(50), nullable=False, unique=True, comment="Role code")
    role_name = Column(String(100), nullable=False, comment="Role name")
    role_desc = Column(String(500), comment="Role description")
    status = Column(Integer, default=1, comment="Status: 1=Active, 0=Disabled")
    is_deleted = Column(Integer, default=0, comment="Delete flag")
    remark = Column(String(500), comment="Remark")
    created_by = Column(Integer, comment="Creator ID")
    updated_by = Column(Integer, comment="Updater ID")
    gmt_created = Column(DateTime, default=datetime.now, comment="Create time")
    gmt_modified = Column(
        DateTime, default=datetime.now, onupdate=datetime.now, comment="Update time"
    )

    def __repr__(self):
        return f"RoleEntity(id={self.id}, role_code='{self.role_code}', role_name='{self.role_name}')"


class RoleDao(BaseDao[RoleEntity, RoleRequest, RoleResponse]):
    """Role DAO"""

    def __init__(self, serve_config: ServeConfig):
        super().__init__()
        self._serve_config = serve_config

    def from_request(
        self, request: Union[RoleRequest, Dict[str, Any]]
    ) -> RoleEntity:
        """Convert request to entity"""
        request_dict = (
            request.to_dict() if isinstance(request, RoleRequest) else request
        )
        # Filter out fields that don't belong to RoleEntity
        entity_fields = {
            'id', 'role_code', 'role_name', 'role_desc', 'status',
            'is_deleted', 'remark', 'created_by', 'updated_by'
        }
        filtered_dict = {k: v for k, v in request_dict.items() if k in entity_fields}
        entity = RoleEntity(**filtered_dict)
        return entity

    def to_request(self, entity: RoleEntity) -> RoleRequest:
        """Convert entity to request"""
        return RoleRequest(
            id=entity.id,
            role_code=entity.role_code,
            role_name=entity.role_name,
            role_desc=entity.role_desc,
            status=entity.status,
            remark=entity.remark,
        )

    def to_response(self, entity: RoleEntity) -> RoleResponse:
        """Convert entity to response"""
        return RoleResponse(
            id=entity.id,
            role_code=entity.role_code,
            role_name=entity.role_name,
            role_desc=entity.role_desc,
            status=entity.status,
            remark=entity.remark,
            gmt_created=entity.gmt_created.isoformat() if entity.gmt_created else None,
            gmt_modified=entity.gmt_modified.isoformat()
            if entity.gmt_modified
            else None,
        )

    def get_role_menus(self, session: Session, role_id: int) -> List[int]:
        """Get role menu IDs"""
        results = (
            session.query(RoleMenuEntity.menu_id)
            .filter(RoleMenuEntity.role_id == role_id)
            .all()
        )
        return [r[0] for r in results]

    def get_role_apps(self, session: Session, role_id: int) -> List[str]:
        """Get role application codes"""
        results = (
            session.query(RoleAppEntity.app_code)
            .filter(RoleAppEntity.role_id == role_id)
            .all()
        )
        return [r[0] for r in results]


# ========================================
# User-Role Relationship Entity
# ========================================
class UserRoleEntity(Model):
    """User-Role relationship entity"""

    __tablename__ = SERVER_USER_ROLE_TABLE_NAME
    __table_args__ = (UniqueConstraint("user_id", "role_id", name="uk_user_role"),)

    id = Column(Integer, primary_key=True, autoincrement=True, comment="ID")
    user_id = Column(Integer, nullable=False, comment="User ID")
    role_id = Column(Integer, nullable=False, comment="Role ID")
    gmt_created = Column(DateTime, default=datetime.now, comment="Create time")

    def __repr__(self):
        return f"UserRoleEntity(user_id={self.user_id}, role_id={self.role_id})"


# ========================================
# Menu Entity
# ========================================
class MenuEntity(Model):
    """Menu entity"""

    __tablename__ = SERVER_MENU_TABLE_NAME

    id = Column(Integer, primary_key=True, autoincrement=True, comment="Menu ID")
    parent_id = Column(Integer, default=0, comment="Parent menu ID, 0=root")
    menu_code = Column(String(50), nullable=False, unique=True, comment="Menu code")
    menu_name = Column(String(100), nullable=False, comment="Menu name")
    menu_name_en = Column(String(100), comment="Menu name (English)")
    menu_type = Column(
        Integer, nullable=False, comment="Menu type: 1=Directory, 2=Menu, 3=Button"
    )
    route_path = Column(String(255), comment="Route path")
    component_path = Column(String(255), comment="Component path")
    icon = Column(String(100), comment="Icon")
    sort_order = Column(Integer, default=0, comment="Sort order")
    permission_code = Column(String(100), comment="Permission code")
    status = Column(Integer, default=1, comment="Status: 1=Active, 0=Disabled")
    is_visible = Column(Integer, default=1, comment="Visible: 1=Yes, 0=No")
    is_deleted = Column(Integer, default=0, comment="Delete flag")
    remark = Column(String(500), comment="Remark")
    created_by = Column(Integer, comment="Creator ID")
    updated_by = Column(Integer, comment="Updater ID")
    gmt_created = Column(DateTime, default=datetime.now, comment="Create time")
    gmt_modified = Column(
        DateTime, default=datetime.now, onupdate=datetime.now, comment="Update time"
    )

    def __repr__(self):
        return f"MenuEntity(id={self.id}, menu_code='{self.menu_code}', menu_name='{self.menu_name}')"


class MenuDao(BaseDao[MenuEntity, MenuRequest, MenuResponse]):
    """Menu DAO"""

    def __init__(self, serve_config: ServeConfig):
        super().__init__()
        self._serve_config = serve_config

    def from_request(
        self, request: Union[MenuRequest, Dict[str, Any]]
    ) -> MenuEntity:
        """Convert request to entity"""
        request_dict = (
            request.to_dict() if isinstance(request, MenuRequest) else request
        )
        # Filter out fields that don't belong to MenuEntity
        entity_fields = {
            'id', 'parent_id', 'menu_code', 'menu_name', 'menu_name_en',
            'menu_type', 'route_path', 'component_path', 'icon', 'sort_order',
            'permission_code', 'status', 'is_visible', 'is_deleted', 'remark',
            'created_by', 'updated_by'
        }
        filtered_dict = {k: v for k, v in request_dict.items() if k in entity_fields}
        entity = MenuEntity(**filtered_dict)
        return entity

    def to_request(self, entity: MenuEntity) -> MenuRequest:
        """Convert entity to request"""
        return MenuRequest(
            id=entity.id,
            parent_id=entity.parent_id,
            menu_code=entity.menu_code,
            menu_name=entity.menu_name,
            menu_name_en=entity.menu_name_en,
            menu_type=entity.menu_type,
            route_path=entity.route_path,
            component_path=entity.component_path,
            icon=entity.icon,
            sort_order=entity.sort_order,
            permission_code=entity.permission_code,
            status=entity.status,
            is_visible=entity.is_visible,
            remark=entity.remark,
        )

    def to_response(self, entity: MenuEntity) -> MenuResponse:
        """Convert entity to response"""
        return MenuResponse(
            id=entity.id,
            parent_id=entity.parent_id,
            menu_code=entity.menu_code,
            menu_name=entity.menu_name,
            menu_name_en=entity.menu_name_en,
            menu_type=entity.menu_type,
            route_path=entity.route_path,
            component_path=entity.component_path,
            icon=entity.icon,
            sort_order=entity.sort_order,
            permission_code=entity.permission_code,
            status=entity.status,
            is_visible=entity.is_visible,
            remark=entity.remark,
            gmt_created=entity.gmt_created.isoformat() if entity.gmt_created else None,
            gmt_modified=entity.gmt_modified.isoformat()
            if entity.gmt_modified
            else None,
        )

    def get_menu_tree(self, session: Session, parent_id: int = 0) -> List[MenuEntity]:
        """Get menu tree"""
        return (
            session.query(MenuEntity)
            .filter(MenuEntity.parent_id == parent_id, MenuEntity.is_deleted == 0)
            .order_by(MenuEntity.sort_order.asc())
            .all()
        )


# ========================================
# Role-Menu Relationship Entity
# ========================================
class RoleMenuEntity(Model):
    """Role-Menu relationship entity"""

    __tablename__ = SERVER_ROLE_MENU_TABLE_NAME
    __table_args__ = (UniqueConstraint("role_id", "menu_id", name="uk_role_menu"),)

    id = Column(Integer, primary_key=True, autoincrement=True, comment="ID")
    role_id = Column(Integer, nullable=False, comment="Role ID")
    menu_id = Column(Integer, nullable=False, comment="Menu ID")
    gmt_created = Column(DateTime, default=datetime.now, comment="Create time")

    def __repr__(self):
        return f"RoleMenuEntity(role_id={self.role_id}, menu_id={self.menu_id})"


# ========================================
# Role-App Relationship Entity
# ========================================
class RoleAppEntity(Model):
    """Role-App relationship entity"""

    __tablename__ = "sys_role_app"
    __table_args__ = (UniqueConstraint("role_id", "app_code", name="uk_role_app"),)

    id = Column(Integer, primary_key=True, autoincrement=True, comment="ID")
    role_id = Column(Integer, nullable=False, comment="Role ID")
    app_code = Column(String(255), nullable=False, comment="Application code")
    gmt_created = Column(DateTime, default=datetime.now, comment="Create time")

    def __repr__(self):
        return f"RoleAppEntity(role_id={self.role_id}, app_code='{self.app_code}')"


# ========================================
# Data Permission Entity
# ========================================
class DataPermissionEntity(Model):
    """Data permission entity"""

    __tablename__ = SERVER_DATA_PERMISSION_TABLE_NAME
    __table_args__ = (
        UniqueConstraint("role_id", "resource_type", name="uk_role_resource"),
    )

    id = Column(Integer, primary_key=True, autoincrement=True, comment="ID")
    role_id = Column(Integer, nullable=False, comment="Role ID")
    resource_type = Column(
        String(50),
        nullable=False,
        comment="Resource type: chat_history, knowledge, app, etc.",
    )
    permission_type = Column(
        Integer, nullable=False, comment="Permission type: 1=All, 2=Own, 3=Custom"
    )
    resource_ids = Column(Text, comment="Resource IDs (JSON array)")
    remark = Column(String(500), comment="Remark")
    gmt_created = Column(DateTime, default=datetime.now, comment="Create time")
    gmt_modified = Column(
        DateTime, default=datetime.now, onupdate=datetime.now, comment="Update time"
    )

    def __repr__(self):
        return f"DataPermissionEntity(role_id={self.role_id}, resource_type='{self.resource_type}')"


# ========================================
# Login Log Entity
# ========================================
class LoginLogEntity(Model):
    """Login log entity"""

    __tablename__ = SERVER_LOGIN_LOG_TABLE_NAME

    id = Column(Integer, primary_key=True, autoincrement=True, comment="Log ID")
    user_id = Column(Integer, comment="User ID")
    username = Column(String(50), comment="Username")
    ip_address = Column(String(50), comment="IP address")
    login_location = Column(String(255), comment="Login location")
    browser = Column(String(100), comment="Browser")
    os = Column(String(100), comment="Operating system")
    status = Column(Integer, default=1, comment="Status: 1=Success, 0=Failed")
    message = Column(String(255), comment="Message")
    login_time = Column(DateTime, default=datetime.now, comment="Login time")

    def __repr__(self):
        return f"LoginLogEntity(user_id={self.user_id}, username='{self.username}')"


# ========================================
# Operation Log Entity
# ========================================
class OperationLogEntity(Model):
    """Operation log entity"""

    __tablename__ = SERVER_OPERATION_LOG_TABLE_NAME

    id = Column(Integer, primary_key=True, autoincrement=True, comment="Log ID")
    user_id = Column(Integer, comment="User ID")
    username = Column(String(50), comment="Username")
    operation_type = Column(String(50), nullable=False, comment="Operation type")
    operation_desc = Column(String(255), comment="Operation description")
    method = Column(String(255), comment="Request method")
    request_url = Column(String(500), comment="Request URL")
    request_params = Column(Text, comment="Request parameters")
    response_data = Column(Text, comment="Response data")
    ip_address = Column(String(50), comment="IP address")
    status = Column(Integer, default=1, comment="Status: 1=Success, 0=Failed")
    error_msg = Column(Text, comment="Error message")
    execute_time = Column(Integer, comment="Execute time (ms)")
    operation_time = Column(DateTime, default=datetime.now, comment="Operation time")

    def __repr__(self):
        return f"OperationLogEntity(user_id={self.user_id}, operation_type='{self.operation_type}')"


# ========================================
# Serve DAO (for compatibility)
# ========================================
class ServeDao(UserDao):
    """Serve DAO for compatibility"""

    pass
